package com.kamistoat.meimeimall.meimeimallseckill.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSON;
import com.kamistoat.meimeimall.common.utils.R;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Sentinel自定义全局流控/熔断/降级处理。实现Sentinel的库类BlockExceptionHandler，对Sentinel抛出的BlockException异常进行处理。
 */
@Configuration
public class SecKillSentinelConfig implements BlockExceptionHandler {

    /**
     * @param httpServletRequest  被流控/熔断/降级阻塞的请求
     * @param httpServletResponse 返回给请求的响应
     * @param blockException      Sentinel抛出的流量阻塞异常
     * @throws Exception
     */
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException blockException) throws Exception {
        // 给这个响应设置重定向路径。一般是流量过大时，直接重定向到一个提示用户等待的页面
        // httpServletResponse.sendRedirect("http://meimeimall.com/blockWait.html");

        // 也可以正常返回。调用方根据返回的状态码判断到底是被流控/熔断/降级，总而执行不同的处理。
        // 不过这要求所有的远程调用都是通过 R对象 通信，不能直接写映射类
        int sentinelErrCode = 0;
        int httpStatusCode = 0;
        String sentinelErrMsg = "";

        if (blockException instanceof FlowException) {
            // 429--TooManyRequests
            httpStatusCode = HttpStatus.TOO_MANY_REQUESTS.value();
            sentinelErrCode = 429001;
            sentinelErrMsg = "请求被限流，请稍后重试";
        } else if (blockException instanceof DegradeException) {
            // 503-ServiceUnavailable
            httpStatusCode = HttpStatus.SERVICE_UNAVAILABLE.value();
            sentinelErrCode = 503001;
            sentinelErrMsg = "请求被熔断/降级，启用降级方案";
        } else if (blockException instanceof ParamFlowException) {
            // 429--TooManyRequests
            httpStatusCode = HttpStatus.TOO_MANY_REQUESTS.value();
            sentinelErrCode = 429002;
            sentinelErrMsg = "请求被热点限流保护，请稍后重试";
        } else if (blockException instanceof SystemBlockException) {
            // 429--TooManyRequests
            httpStatusCode = HttpStatus.TOO_MANY_REQUESTS.value();
            sentinelErrCode = 429003;
            sentinelErrMsg = "请求被系统规则限流保护，请稍后重试";
        } else if (blockException instanceof AuthorityException) {
            // 401--Unauthorized
            httpStatusCode = HttpStatus.UNAUTHORIZED.value();
            sentinelErrCode = 401001;
            sentinelErrMsg = "请求未通过访问限制";
        }

        // 构建一个R对象，携带状态码返回
        R blocked = R.error(sentinelErrCode, sentinelErrMsg);
        // 设置响应的编码信息
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json");
        // 设置HttpStatus
        httpServletResponse.setStatus(httpStatusCode);
        // 把这个R对象写到响应中去
        httpServletResponse.getWriter().write(JSON.toJSONString(blocked));

        // 日志记录限流情况
    }
}
